/*************************************************************************
 * Nuvoton Electronics Corporation confidential
 *
 * Copyright (c) 2008 by Nuvoton Electronics Corporation
 * All rights reserved
 *
 * FILENAME
 *     gpio_lib.c
 *
 * VERSION
 *     1.0
 *
 * DESCRIPTION
 *     NUC900 GPIO library
 *
 * HISTORY
 *     2008.06.24       Created
 *
 * REMARK
 *     None
 **************************************************************************/
#ifdef ECOS
#include "stdio.h"
#include "drv_api.h"
#include "diag.h"
#include "wbtypes.h"
#include "wbio.h"
#else
#include "wblib.h"
#endif
#include "nuc900_reg.h"
#include "nuc900_gpio.h"


#ifdef ECOS
#define IRQ_GROUP0   		2
#define IRQ_GROUP1   		3
cyg_interrupt  gpio_irq0_interrupt, gpio_irq1_interrupt;
cyg_handle_t   gpio_irq0_handle, gpio_irq1_handle;
#endif

/*---------------------------------------------------------------------------------------------------------*/
/* Macro, type and constant definitions                                                                    */
/*---------------------------------------------------------------------------------------------------------*/

#define NULL 0
#define SET_CFG(reg, bitNumber, cfg)   (((reg) & (~(0x3UL << (bitNumber) * 2))) | (cfg << (bitNumber) * 2))

static GPIO_CFG gpioCfg;
static INT32 maskTbl[] = {GPIOC_MASK, GPIOD_MASK, GPIOE_MASK, GPIOF_MASK, GPIOG_MASK, GPIOH_MASK, GPIOI_MASK};


#ifdef ECOS
static cyg_uint32 GPIO_IRQHandler0(cyg_vector_t vector, cyg_addrword_t data)
#else
VOID GPIO_IRQHandler0(VOID)
#endif
{
	UINT32 reg;
	INT32 i;
	INT32 mask;

	reg = inpw(REG_AIC_GEN) & inpw(REG_AIC_GASR);
	for(i=NIRQ0;i<=NIRQ3;i++)
	{
		mask = 1 << i;
		if((reg & mask) != 0)
		{
			if(gpioCfg.IRQCallback[i] != NULL)
			{
				gpioCfg.IRQCallback[i](reg, gpioCfg.IRQUserData[i]);
			}
		}
		
		/* The nInt should be clear if it is edge trigger */
		outpw(REG_AIC_GSCR, mask);
	}
#ifdef ECOS	
	cyg_drv_interrupt_acknowledge(IRQ_GROUP0);
	return CYG_ISR_HANDLED;
#endif
}


#ifdef ECOS
static cyg_uint32 GPIO_IRQHandler1(cyg_vector_t vector, cyg_addrword_t data)
#else
VOID GPIO_IRQHandler1(VOID)
#endif
{
	UINT32 reg;
	INT32 i;
	INT32 mask;

	reg = inpw(REG_AIC_GEN) & inpw(REG_AIC_GASR);
	for(i=NIRQ4;i<=NIRQ7;i++)
	{
		mask = 1 << i;
		if((reg & mask) != 0)
		{
			if(gpioCfg.IRQCallback[i] != NULL)
			{
				gpioCfg.IRQCallback[i](reg, gpioCfg.IRQUserData[i]);
			}
		}
		
		/* The nInt should be clear if it is edge trigger */
		outpw(REG_AIC_GSCR, mask);
	}
#ifdef ECOS	
	cyg_drv_interrupt_acknowledge(IRQ_GROUP1);
	return CYG_ISR_HANDLED;
#endif
}


/*---------------------------------------------------------------------------------------------------------*/
/* Function: GPIO_InitFunction                                                                             */
/*                                                                                                         */
/* Parameters:                                                                                             */
/*               GPIO_FUNCTION function                                                                    */
/*                                                                                                         */
/* Returns:      None                                                                                      */
/* Side effects: the_effect                                                                                */
/* Description:                                                                                            */
/*               The function is used to configure IP's IO or GPIO                                         */
/*---------------------------------------------------------------------------------------------------------*/
INT32 GPIO_InitFunction(GPIO_FUNCTION function)
{
	UINT32 reg;
	INT32 status;
	INT32 pdid;

	pdid = inpw(REG_PDID);

/*---------------------------------------------------------------------------------------------------------*/
/* Init to IP functions                                                                                    */
/*---------------------------------------------------------------------------------------------------------*/
	
	status = SUCCESSFUL;    
	switch(function)
	{
		case FUNC_GPIO:
		{   
			/* All multi-function pin is assigned to GPIO                                                  */
			outpw(REG_MFSEL, 0x0);
			break;        
		}
		case FUNC_RMII:
		{
			reg = inpw(REG_MFSEL);
			reg = reg | 0x2;            
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_NAND:
		{
			reg = inpw(REG_MFSEL);
			reg = (reg & (~0xC)) | 0x4;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_KPI:
		{
			reg = inpw(REG_MFSEL);
			reg = (reg & (~0xC)) | 0x8;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_LCD:
		{
			reg = inpw(REG_MFSEL);
			reg = (reg & (~0xC)) | 0xC;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_SC0:
		{
			reg = inpw(REG_MFSEL);
			reg = (reg & (~0x30)) | 0x10;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_SD0:
		{
			reg = inpw(REG_MFSEL);
			reg = (reg & (~0xF0)) | 0xA0;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_SD1:
		{
			reg = inpw(REG_MFSEL);
			reg = (reg & (~0x3C0000)) | 0x80001;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_MEMSTICK0:
		{
			reg = inpw(REG_MFSEL);
			reg = (reg & (~0xF0)) | 0xF0;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_MEMSTICK1:
		{
			reg = inpw(REG_MFSEL);
			reg = (reg & (~0x3C0000)) | 0x1C0001;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_SC1:
		{
			reg = inpw(REG_MFSEL);
			reg = (reg & (~0xC0)) | 0x40;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_PCI:
		{
			reg = inpw(REG_MFSEL);
			reg = (reg & (~0xF0)) | 0x50;
			reg = reg | 0x3000;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_UART0:
		{
			reg = inpw(REG_MFSEL);
			reg = reg & (~0x2000);
			reg = reg | 0x100;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_UART1:
		{
			reg = inpw(REG_MFSEL);
			reg = reg & (~0x2000);
			reg = reg | 0x200;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_UART2:
		{
			reg = inpw(REG_MFSEL);
			reg = reg & (~0x2000);
			reg = reg | 0x400;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_UART3:
		{
			reg = inpw(REG_MFSEL);
			reg = reg | 0x800;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_UART4:
		{
			reg = inpw(REG_MFSEL);
			reg = reg & (~0x2000);
			reg = reg | 0x1000;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_MODEM:
		{
			reg = inpw(REG_MFSEL);
			reg = reg | 0x2000;
			reg = reg | 0x1600;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_XDMA0:
		{
			reg = inpw(REG_MFSEL);
			reg = (reg & (~0xC0000)) | 0x40000;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_XDMA1:
		{
			reg = inpw(REG_MFSEL);
			reg = (reg & (~0x80001)) | 0x80000;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_USI:
		{
			reg = inpw(REG_MFSEL);
			reg = (reg & (~0x3C000)) | 0x28000;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_I2C0:
		{
			reg = inpw(REG_MFSEL);
			reg = (reg & (~0xC000)) | 0x4000;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_I2C1:
		{
			reg = inpw(REG_MFSEL);
			reg = (reg & (~0x30000)) | 0x10000;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_PS20:
		{
			reg = inpw(REG_MFSEL);
			reg = (reg & (~0x100001)) | 0x100000;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_PS21:
		{
			reg = inpw(REG_MFSEL);
			reg = (reg & (~0x200001)) | 0x200000;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_I2S:
		{
			reg = inpw(REG_MFSEL);
			reg = (reg & (~0xC00000)) | 0xC00000;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_AC97:
		{
			reg = inpw(REG_MFSEL);
			reg = (reg & (~0xC00000)) | 0x800000;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_PWM:
		{
			reg = inpw(REG_MFSEL);
			reg = (reg & (~0xC00000)) | 0x400000;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_NIRQ0_3:
		{
			reg = inpw(REG_MFSEL);
			reg = reg | 0x1000000;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_NIRQ4_7:
		{
			reg = inpw(REG_MFSEL);
			reg = reg | 0x2000000;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_ATAPI:
		{
			reg = inpw(REG_MFSEL);
			reg = reg | 0x4000000;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_NWDOG:
		{
			reg = inpw(REG_MFSEL);
			reg = reg | 0x8000000;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_USBPHY0:
		{
			reg = inpw(REG_MFSEL);
			reg = reg & (~0x30000000);
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_USBPHY0DBG:
		{
			reg = inpw(REG_MFSEL);
			reg = (reg & (~0x30000000)) | 0x10000000;
			outpw(REG_MFSEL, reg);
			break;
		}
		case FUNC_EXT_USBPHY0:
		{
			reg = inpw(REG_MFSEL);
			reg = (reg & (~0x30000000)) | 0x20000000;
			outpw(REG_MFSEL, reg);
		}
		default:
		{
			status = GPIO_ERR_UNSUPPORTED;   
			break;
		}
	}    

	return status;
}



INT32 GPIO_Open(GPIO_PORT port, GPIO_DIR direction, GPIO_PULL pull)
{
	UINT32 mask;
	INT32 portIndex;
	INT32 offset;
	
	offset = (INT32)port;
	portIndex = port / GPIOD;
	mask = maskTbl[portIndex];    
	
	if(gpioCfg.bitBusyFlag[portIndex] != 0)
	{
		return GPIO_ERR_PORT_BUSY;
	}
	
	/* Set all bits in the port to busy                                                                    */
	gpioCfg.bitBusyFlag[portIndex] = mask;
	
	
	/* Set port direction                                                                                  */ 
	if(direction == DIR_OUTPUT)
	{
		outpw(REG_GPIOC_DIR + offset, mask); 
	}
	else
	{
		outpw(REG_GPIOC_DIR + offset, 0); 
	}

	if(pull == PULL_UP)
	{
		outpw(REG_GPIOCPE + offset, mask);
	}
	else
	{
		outpw(REG_GPIOCPE + offset, 0);
	}

	return SUCCESSFUL;
}


INT32 GPIO_Close(GPIO_PORT port)
{
	INT32 offset;
	INT32 portIndex;
	INT32 mask;

	offset = (INT32)port;    
	portIndex = port / GPIOD;
	mask = maskTbl[portIndex];
	
	/* Clean the busy flag                                                                                 */
	gpioCfg.bitBusyFlag[portIndex] = 0;
	
	/* Reset to input */    
	outpw(REG_GPIOC_DIR + offset, 0);

	/* Reset to pull-up */
	outpw(REG_GPIOCPE + portIndex * 4, mask); 

	return SUCCESSFUL;
}


INT32 GPIO_Set(GPIO_PORT port, UINT32 bitMap)
{
	INT32 offset;
	INT32 reg;
	
	offset = (INT32)port;
	
	reg = inpw(REG_GPIOC_DATAOUT + offset);
	reg = reg | bitMap;
	outpw(REG_GPIOC_DATAOUT + offset, reg);

	return SUCCESSFUL;
}

INT32 GPIO_Clr(GPIO_PORT port, UINT32 bitMap)
{
	INT32 offset;
	INT32 reg;
	
	offset = (INT32)port;
	
	reg = inpw(REG_GPIOC_DATAOUT + offset);
	reg = reg & (~bitMap);
	outpw(REG_GPIOC_DATAOUT + offset, reg);

	return SUCCESSFUL;
}

UINT32 GPIO_ReadPort(GPIO_PORT port)
{
	UINT32 reg;
	INT32 offset;
	
	offset = (INT32)port;
	
	reg = inpw(REG_GPIOC_DATAIN + offset);

	return reg;
}

INT32 GPIO_SetPortDir(GPIO_PORT port, GPIO_DIR direction)
{
	INT32 offset;
	INT32 mask;
	INT32 portIndex;
	
	offset = (INT32)port;
	portIndex = port / GPIOD;
	mask = maskTbl[portIndex];
	
	if(direction == DIR_OUTPUT)
	{
		outpw(REG_GPIOC_DIR + offset, mask);
	}
	else
	{
		outpw(REG_GPIOC_DIR + offset, 0x0);
	}

	return SUCCESSFUL;
}

INT32 GPIO_OpenBit(GPIO_PORT port, GPIO_BIT bit, GPIO_DIR direction, GPIO_PULL pull)
{
	UINT32 reg;
	UINT32 mask;
	INT32 portIndex;
	INT32 offset;
	
	offset = (INT32)port;
	portIndex = port / GPIOD;
	mask = (UINT32)bit;
	
	if((gpioCfg.bitBusyFlag[portIndex]&mask) != 0)
	{
		return GPIO_ERR_BIT_BUSY;
	}

	gpioCfg.bitBusyFlag[portIndex] = gpioCfg.bitBusyFlag[portIndex] | mask;
	
	reg = inpw(REG_GPIOC_DIR + offset);
	reg = reg & (~mask);

	if(direction == DIR_OUTPUT)
	{
		reg = reg | mask;
	}
	
	outpw(REG_GPIOC_DIR + offset, reg);

	reg = inpw(REG_GPIOCPE + portIndex * 4);
	reg = reg & (~mask);

	if(pull == PULL_UP)
	{
		reg = reg | mask;
	}

	outpw(REG_GPIOCPE + portIndex * 4, reg);

	return SUCCESSFUL;
}

 
INT32 GPIO_CloseBit(GPIO_PORT port, GPIO_BIT bit)
{
	UINT32 reg;
	UINT32 mask;
	INT32 portIndex;
	INT32 offset;
	
	offset = (INT32)port;
	portIndex = port / GPIOD;
	mask = (UINT32)bit;
	
	gpioCfg.bitBusyFlag[portIndex] = gpioCfg.bitBusyFlag[portIndex] & (~mask);
	
	reg = inpw(REG_GPIOC_DIR + offset);
	reg = reg & (~mask);
	outpw(REG_GPIOC_DIR + offset, reg);

	reg = inpw(REG_GPIOCPE + portIndex * 4);
	reg = reg & (~mask);
	outpw(REG_GPIOCPE + portIndex * 4, reg);

	return SUCCESSFUL;
}


INT32 GPIO_SetBit(GPIO_PORT port, GPIO_BIT bit)
{
	UINT32 bitMap;
	INT32 offset;
	INT32 reg;
	
	offset = (INT32)port;
	bitMap = (UINT32)bit;
	
	reg = inpw(REG_GPIOC_DATAOUT + offset);
	reg = reg | bitMap;
	outpw(REG_GPIOC_DATAOUT + offset, reg);

	return SUCCESSFUL;
}

INT32 GPIO_ClrBit(GPIO_PORT port, GPIO_BIT bit)
{
	UINT32 bitMap;
	INT32 offset;
	INT32 reg;
	
	offset = (INT32)port;
	bitMap = (UINT32)bit;
	
	reg = inpw(REG_GPIOC_DATAOUT + offset);
	reg = reg & (~bitMap);
	outpw(REG_GPIOC_DATAOUT + offset, reg);

	return SUCCESSFUL;
}

INT32 GPIO_ReadBit(GPIO_PORT port, GPIO_BIT bit)
{
	UINT32 reg;
	UINT32 bitMap;
	INT32 offset;    

	offset = (INT32)port;
	bitMap = (UINT32)bit;
	
	reg = inpw(REG_GPIOC_DATAIN + offset);
	
	return ((reg & bitMap)?1:0);
}

INT32 GPIO_SetBitDir(GPIO_PORT port, GPIO_BIT bit, GPIO_DIR direction)
{
	UINT32 reg;
	UINT32 bitMap;
	INT32 offset;
	
	offset = (INT32)port;
	bitMap = (UINT32)bit;
	
	reg = inpw(REG_GPIOC_DIR + offset);
	reg = reg & (~bitMap);
	
	if(direction == DIR_OUTPUT)
	{
		reg = reg | bitMap;
	}

	outpw(REG_GPIOC_DIR + offset, reg);

	return SUCCESSFUL;
}

BOOL GPIO_BitIsUsed(GPIO_PORT port, GPIO_BIT bit)
{
	UINT32 mask;
	INT32 portIndex;
	INT32 offset;
	
	offset = (INT32)port;
	portIndex = port / GPIOD;
	mask = (UINT32)bit;
	
	return ((gpioCfg.bitBusyFlag[portIndex] & mask)?TRUE:FALSE);
}


INT32 GPIO_EnableInt(GPIO_NIRQ nIrq, GPIO_CALLBACK callback, UINT32 userData)
{
	INT i;

	for(i=NIRQ0;i<=NIRQ7;i++)
	{
		if(i == nIrq)
		{
			if(callback != NULL)
			{
				gpioCfg.IRQCallback[i] = (GPIO_CALLBACK)callback;
				gpioCfg.IRQUserData[i] = userData;
			}
#ifdef ECOS
			outpw(REG_AIC_GEN, inpw(REG_AIC_GEN) | (1 << i));
#else	
			sysEnableGroupInterrupt(1 << i);
#endif
		}
	}

	if((nIrq >= NIRQ0) && (nIrq <= NIRQ3))
	{    
#ifdef ECOS
		cyg_drv_interrupt_create(IRQ_GROUP0, 8, 0, GPIO_IRQHandler0, 0,
								&gpio_irq0_handle, &gpio_irq0_interrupt);
    	cyg_drv_interrupt_attach(gpio_irq0_handle);
    	cyg_drv_interrupt_unmask(IRQ_GROUP0);
#else		
		sysInstallISR(IRQ_LEVEL_7, IRQ_GROUP0, (PVOID)GPIO_IRQHandler0);
		sysEnableInterrupt(IRQ_GROUP0);
#endif		
	}
	else
	{
#ifdef ECOS
		cyg_drv_interrupt_create(IRQ_GROUP1, 8, 0, GPIO_IRQHandler1, 0,
								&gpio_irq1_handle, &gpio_irq1_interrupt);
    	cyg_drv_interrupt_attach(gpio_irq1_handle);
    	cyg_drv_interrupt_unmask(IRQ_GROUP1);
#else		
		sysInstallISR(IRQ_LEVEL_7, IRQ_GROUP1, (PVOID)GPIO_IRQHandler1);
		sysEnableInterrupt(IRQ_GROUP1);
#endif
	}
	
	return SUCCESSFUL;  
}

INT32 GPIO_SetTriggerType(GPIO_NIRQ nIrq, GPIO_TRIGGER_TYPE triggerType)
{
	INT32 reg;

	reg = inpw(REG_AIC_IRQSC);
	reg = (reg & (~(0x3 << nIrq * 2))) | (triggerType << nIrq * 2);
	outpw(REG_AIC_IRQSC, reg);
	
	return SUCCESSFUL;
}


INT32 GPIO_DisableInt(GPIO_NIRQ nIrq)
{
	INT i;

	for(i=NIRQ0;i<=NIRQ7;i++)
	{
		if(i == nIrq)
		{
#ifdef ECOS
			outpw(REG_AIC_GEN, inpw(REG_AIC_GEN) & ~(1 << i));
#else	
			sysDisableGroupInterrupt(1<<i);
#endif
			gpioCfg.IRQCallback[i] = NULL;
			gpioCfg.IRQUserData[i] = 0;
		}
	}

	return SUCCESSFUL;  
}

INT32 GPIO_EnableIntDebounce(GPIO_NIRQ nIrq, INT32 debounceClkSel)
{
	UINT32 reg;
	UINT32 mask;
	INT32 IntNumber;
	
	IntNumber = (UINT32)nIrq;
	
	mask = 1 << IntNumber;

	reg = inpw(REG_GPIOH_DBNCE);
/*---------------------------------------------------------------------------------------------------------*/
/* Setting the debounce timing                                                                             */
/*---------------------------------------------------------------------------------------------------------*/
	reg = reg & (~0x700UL) |  ((debounceClkSel&0x7) << 8);
/*---------------------------------------------------------------------------------------------------------*/
/* Enable the debounce function                                                                            */
/*---------------------------------------------------------------------------------------------------------*/
	reg = reg | mask;
	outpw(REG_GPIOH_DBNCE, reg);
	
	return SUCCESSFUL;
}

INT32 GPIO_DisableIntDebounce(GPIO_NIRQ nIrq)
{
	UINT32 reg;
	UINT32 mask;
	
	mask = 1 << (UINT32)nIrq;
	
	reg = inpw(REG_GPIOH_DBNCE);
	reg = reg & (~mask);
	outpw(REG_GPIOH_DBNCE, reg);
	
	return SUCCESSFUL;
}




